package com.dimples.dd.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.dynamic.datasource.creator.DataSourceProperty;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dimples.dd.common.constant.DDConstant;
import com.dimples.dd.common.exception.ErrorCode;
import com.dimples.dd.common.model.Option;
import com.dimples.dd.common.pojo.PageResult;
import com.dimples.dd.mybatis.core.enums.DbDriverEnum;
import com.dimples.dd.mybatis.core.query.LambdaQueryWrapperX;
import com.dimples.dd.mybatis.core.util.JdbcSecurityUtil;
import com.dimples.dd.mybatis.core.util.JdbcUtils;
import com.dimples.dd.system.convert.DataSourceConfigConvert;
import com.dimples.dd.system.mapper.SystemDataSourceConfigMapper;
import com.dimples.dd.system.model.entity.SystemDataSourceConfigDO;
import com.dimples.dd.system.model.form.DataSourceConfigForm;
import com.dimples.dd.system.model.query.DataSourceConfigPageQuery;
import com.dimples.dd.system.model.vo.DataSourceConfigPageVO;
import com.dimples.dd.system.service.SystemDataSourceConfigService;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.sql.SQLException;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.dimples.dd.common.exception.BizExceptionUtil.exception;
import static com.dimples.dd.common.exception.ErrorCode.DATA_SOURCE_CONFIG_NOT_EXISTS;
import static com.dimples.dd.common.exception.ErrorCode.DATA_SOURCE_CONFIG_NOT_OK;

@RequiredArgsConstructor
@Service
public class SystemDataSourceConfigServiceImpl extends ServiceImpl<SystemDataSourceConfigMapper, SystemDataSourceConfigDO> implements SystemDataSourceConfigService {

    private final DataSourceConfigConvert dataSourceConfigConvert;
    @Resource
    private DynamicDataSourceProperties dynamicDataSourceProperties;

    @Override
    public PageResult<DataSourceConfigPageVO> selectDataSourceConfigPage(DataSourceConfigPageQuery queryParams) {
        PageResult<SystemDataSourceConfigDO> configDOPageResult = this.baseMapper.selectPage(queryParams,
                new LambdaQueryWrapperX<SystemDataSourceConfigDO>()
                        .eqIfPresent(SystemDataSourceConfigDO::getCode, queryParams.getCode())
                        .eqIfPresent(SystemDataSourceConfigDO::getDbType, queryParams.getDbType())
                        .likeIfPresent(SystemDataSourceConfigDO::getName, queryParams.getName())
        );
        if (CollUtil.isEmpty(configDOPageResult.getData().getRecords())) {
            configDOPageResult.getData().setRecords(CollUtil.newArrayList(buildMasterDataSourceConfig()));
        } else {
            configDOPageResult.getData().getRecords().add(0, buildMasterDataSourceConfig());
        }
        return this.dataSourceConfigConvert.entity2PageVo(configDOPageResult);
    }

    @Override
    public List<Option<String>> listDataSourceConfigOptions() {
        // 查询数据
        List<SystemDataSourceConfigDO> configDOList = this
                .list(new LambdaQueryWrapper<SystemDataSourceConfigDO>()
                        .select(SystemDataSourceConfigDO::getCode, SystemDataSourceConfigDO::getName)
                );

        // 实体转换
        return dataSourceConfigConvert.entities2Options(configDOList);
    }

    @Override
    public DataSourceConfigForm selectDataSourceConfigForm(Long id) {
        // 如果 id 为 0，默认为 master 的数据源
        if (Objects.equals(id, SystemDataSourceConfigDO.ID_MASTER)) {
            SystemDataSourceConfigDO dataSourceConfigDO = buildMasterDataSourceConfig();
            return this.dataSourceConfigConvert.entity2Form(dataSourceConfigDO);
        }
        // 从 DB 中读取
        SystemDataSourceConfigDO dataSourceConfigDO = this.baseMapper.selectById(id);
        DataSourceConfigForm dataSourceConfigForm = this.dataSourceConfigConvert.entity2Form(dataSourceConfigDO);
        //密码解密
        String dbPassword = dataSourceConfigDO.getDbPassword();
        if (StrUtil.isNotBlank(dbPassword)) {
            try {
                String decodedStr = SecureUtil.aes(StrUtil.bytes(DDConstant.DD_DEFAULT_AES_KEY)).decryptStr(dbPassword);
                dataSourceConfigForm.setDbPassword(decodedStr);
            } catch (Exception e) {
                dataSourceConfigForm.setDbPassword(null);
            }
        }
        return dataSourceConfigForm;
    }

    private SystemDataSourceConfigDO buildMasterDataSourceConfig() {
        String primary = dynamicDataSourceProperties.getPrimary();
        DataSourceProperty dataSourceProperty = dynamicDataSourceProperties.getDatasource().get(primary);
        return new SystemDataSourceConfigDO().setId(SystemDataSourceConfigDO.ID_MASTER).setName(primary)
                .setDbUrl(dataSourceProperty.getUrl())
                .setDbUsername(dataSourceProperty.getUsername())
                .setDbPassword(dataSourceProperty.getPassword())
                .setDbType(DbDriverEnum.MYSQL_.getValue())
                .setDbDriver(DbDriverEnum.MYSQL_.getLabel())
                ;
    }

    @Override
    public boolean createDataSourceConfig(DataSourceConfigForm dataSourceConfigForm) {
        // jdbc连接地址漏洞问题
        JdbcSecurityUtil.validate(dataSourceConfigForm.getDbUrl());

        this.validateDataSourceConfigCodeExists(dataSourceConfigForm.getCode());
        if (StrUtil.isBlank(dataSourceConfigForm.getDbDriver())) {
            try {
                dataSourceConfigForm.setDbDriver(com.alibaba.druid.util.JdbcUtils.getDriverClassName(dataSourceConfigForm.getDbUrl()));
            } catch (SQLException e) {
                log.error(ExceptionUtil.stacktraceToString(e));
            }
        }

        SystemDataSourceConfigDO sourceConfigDO = this.dataSourceConfigConvert.from2Entity(dataSourceConfigForm);

        String dbPassword = sourceConfigDO.getDbPassword();
        if (StrUtil.isNotBlank(dbPassword)) {
            String encrypt = SecureUtil.aes(StrUtil.bytes(DDConstant.DD_DEFAULT_AES_KEY)).encryptBase64(dbPassword);
            sourceConfigDO.setDbPassword(encrypt);
        }
        this.save(sourceConfigDO);
        return true;
    }

    private void validateDataSourceConfigCodeExists(String code) {
        if (this.baseMapper.selectCount(SystemDataSourceConfigDO::getCode, code) > 0) {
            throw exception(ErrorCode.DATA_SOURCE_CONFIG_CODE_EXISTS, code);
        }
    }

    private void validateDataSourceConfigExists(List<Long> id) {
        List<SystemDataSourceConfigDO> doList = this.baseMapper.selectBatchIds(id);
        if (CollUtil.isEmpty(doList)) {
            throw exception(DATA_SOURCE_CONFIG_NOT_EXISTS, StrUtil.join(StrUtil.COMMA, id));
        }
        List<Long> noExistId = CollUtil.subtractToList(id, doList.stream().map(SystemDataSourceConfigDO::getId).collect(Collectors.toList()));
        if (CollUtil.isNotEmpty(noExistId)) {
            throw exception(DATA_SOURCE_CONFIG_NOT_EXISTS, StrUtil.join(StrUtil.COMMA, noExistId));
        }
    }

    @Override
    public boolean updateDataSourceConfig(DataSourceConfigForm dataSourceConfigFormForm) {
        // 校验存在
        validateDataSourceConfigExists(CollUtil.newArrayList(dataSourceConfigFormForm.getId()));

        SystemDataSourceConfigDO sourceConfigDO = this.dataSourceConfigConvert.from2Entity(dataSourceConfigFormForm);

        String dbPassword = dataSourceConfigFormForm.getDbPassword();
        if (StrUtil.isNotBlank(dbPassword)) {
            String encrypt = SecureUtil.aes(StrUtil.bytes(DDConstant.DD_DEFAULT_AES_KEY)).encryptBase64(dbPassword);
            sourceConfigDO.setDbPassword(encrypt);
        }
        this.updateById(sourceConfigDO);
        return true;
    }

    @Override
    public boolean deleteDataSourceConfigs(String ids) {
        // 校验存在
        validateDataSourceConfigExists(StrUtil.split(ids, StrUtil.COMMA).stream().map(Long::valueOf).collect(Collectors.toList()));
        // 删除
        this.baseMapper.deleteBatchIds(StrUtil.split(ids, StrUtil.COMMA));
        return true;
    }

    @Override
    public boolean testConnection(DataSourceConfigForm dataSourceConfigForm) {
        try {
            return JdbcUtils.isConnectionOK(dataSourceConfigForm.getDbUrl(), dataSourceConfigForm.getDbUsername(), dataSourceConfigForm.getDbPassword());
        } catch (Exception e) {
            throw exception(DATA_SOURCE_CONFIG_NOT_OK, ExceptionUtil.getMessage(e));
        }
    }
}
